#include "grflib_stdafx.h"
#include "DXUTgui.h"
#include "SDKmisc.h"
#include "../include/XMesh.h"
#include "../include/Effect.h"
#include "../include/D3D10Engine.h"

namespace grflib { namespace engine { namespace aux {

CXMesh::CXMesh()
{}

CXMesh::~CXMesh()
{
	OnDestroy();
}

void CXMesh::SetFilename(TCHAR *fn)
{
	if (fn)
		m_filename = fn;
}

HRESULT CXMesh::OnCreateDevice(ID3D10Device *pd3dDevice, const DXGI_SURFACE_DESC *pBufferSurfaceDesc)
{
	if (m_filename.length() == 0)
		return S_OK;

	HRESULT hr;
	V_RETURN( m_mesh.Create(pd3dDevice, m_filename.c_str(), true) );

	//assuming vertex-normal-texcoord layout for all subsets
	const D3D10_INPUT_ELEMENT_DESC defaultLayout[] = 
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	};

	for (UINT i = 0; i < m_mesh.GetNumSubsets(0); i++)
	{
		boost::shared_ptr<std::vector<D3D10_INPUT_ELEMENT_DESC>> pLayout(new std::vector<D3D10_INPUT_ELEMENT_DESC>);
		pLayout->push_back(defaultLayout[0]);
		pLayout->push_back(defaultLayout[1]);
		pLayout->push_back(defaultLayout[2]);
		m_pVertexLayouts.push_back(pLayout);
	}

	return S_OK;
}

void CXMesh::OnDestroy()
{
	m_mesh.Destroy();
	m_pVertexLayouts.clear();
}

void CXMesh::SetEffectTechnique(const std::string &effectName, const std::string &techName)
{
	m_effectName = effectName;
	m_techniqueName = techName;
}

HRESULT CXMesh::OnRender(ID3D10Device *pd3dDevice,
					const D3DXMATRIX *pWorldMat,
					double fTime,
					float fElapsedTime,
                    UINT pass)
{
    using namespace engine::core;
	HRESULT hr;

	////////////////////////////////////////////////////////////
	//get technique
	boost::shared_ptr<CEffect> &pEffect = boost::dynamic_pointer_cast<CEffect>(_ResrcMan.FindObj(m_effectName));
	if (!pEffect) return E_FAIL;
	ID3D10EffectTechnique *pTech = pEffect->GetTechnique(m_techniqueName);
	if (!pTech)	return E_FAIL;

    D3D10_TECHNIQUE_DESC techDesc;
	pTech->GetDesc(&techDesc);

	////////////////////////////////////////////////////////////
	//set shader variables
	ID3D10EffectMatrixVariable *pProjectMatVar = pEffect->GetShaderVariable("Projection")->AsMatrix();
	ID3D10EffectMatrixVariable *pViewMatVar = pEffect->GetShaderVariable("View")->AsMatrix();
	ID3D10EffectMatrixVariable *pWorldMatVar = pEffect->GetShaderVariable("World")->AsMatrix();
	if (!pViewMatVar || !pProjectMatVar || !pWorldMatVar) //uncompatible shader
	{
		W32ERR(_T("Uncompatible shader!"));
		return E_FAIL;
	}

	boost::shared_ptr<CCamera> &pCurCamera = _CamMan.GetCurrentCamera();
	if (!pCurCamera) //current camera not set
	{
		W32ERR(_T("Current camera not set!"));
		return E_FAIL;
	}

	pProjectMatVar->SetMatrix( (float*)pCurCamera->GetDXUTCamera()->GetProjMatrix() );
	pViewMatVar->SetMatrix( (float*)pCurCamera->GetDXUTCamera()->GetViewMatrix() );
	pWorldMatVar->SetMatrix( (float*)pWorldMat );

	////////////////////////////////////////////////////////////
	//rendering mesh
    UINT Strides[1];
    UINT Offsets[1];
    ID3D10Buffer* pVB[1];
	pVB[0] = m_mesh.GetVB10(0, 0);
	Strides[0] = (UINT)m_mesh.GetVertexStride(0, 0);
	Offsets[0] = 0;
	pd3dDevice->IASetVertexBuffers(0, 1, pVB, Strides, Offsets);
	pd3dDevice->IASetIndexBuffer(m_mesh.GetIB10(0), m_mesh.GetIBFormat10(0), 0);
	
	SDKMESH_SUBSET* pSubset = 0;
	D3D10_PRIMITIVE_TOPOLOGY primTopology;

	ID3D10EffectPass *pPass;
	D3D10_PASS_DESC passDesc;

	for (UINT pass = 0; pass < techDesc.Passes; pass++)
	{
		//IA binding
		pPass = pTech->GetPassByIndex(pass);
		V_RETURN( pPass->GetDesc(&passDesc) );
		V_RETURN( _IAStage.SetInputLayout(pEffect->GetVSSignature(pPass), 
			passDesc.pIAInputSignature, 
			passDesc.IAInputSignatureSize, 
			*m_pVertexLayouts[0]) );

		//actual rendering
		for (UINT subset = 0; subset < m_mesh.GetNumSubsets(0); subset++)
		{
			pSubset = m_mesh.GetSubset(0, subset);
			primTopology = m_mesh.GetPrimitiveType10((SDKMESH_PRIMITIVE_TYPE)pSubset->PrimitiveType);
			pd3dDevice->IASetPrimitiveTopology(primTopology);

			V_RETURN( pTech->GetPassByIndex(pass)->Apply(0) );
			pd3dDevice->DrawIndexed((UINT)pSubset->IndexCount, 0, (UINT)pSubset->VertexStart);
		}
	}

	return S_OK;
}

}}} // namespaces